package com.itic.appbase.framework.plugins.mybatis.plugin;

import java.sql.Connection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;

import org.apache.ibatis.executor.statement.BaseStatementHandler;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.itic.appbase.framework.plugins.mybatis.Page;
import com.itic.appbase.framework.plugins.mybatis.dialect.Dialect;
import com.itic.appbase.framework.plugins.mybatis.dialect.db.MysqlDialect;
import com.itic.appbase.framework.plugins.mybatis.dialect.db.OracleDialect;
import com.itic.appbase.framework.utils.ReflectHelper;
import com.itic.appbase.framework.utils.StringHelper;

/**
 * Mybatis执行SQL切入，判断加载分页SQL.
 * 
 * @ClassName: PagePlugin
 * @author: djp_ivan
 * @date: Apr 15, 2015 2:33:58 PM
 * @version: V1.0
 * @Update_Comments: by djp_ivan at Apr 15, 2015 - description
 */
@SuppressWarnings("unchecked")
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) })
public class PagePlugin
        implements Interceptor {
    protected Logger LOGGER = LoggerFactory.getLogger(getClass());
    private Dialect dialect = null;// 数据库类型
    private String[] exceptKeywords = null;// 不需要进行分页的sql 关键字

    // 处理
    @Override
    public Object intercept(Invocation ivk)
            throws Throwable {
        if (ivk.getTarget() instanceof RoutingStatementHandler) {
            // 获取执行SQL
            RoutingStatementHandler statementHandler = (RoutingStatementHandler) ivk.getTarget();
            BaseStatementHandler delegate = (BaseStatementHandler) ReflectHelper.getValueByFieldName(statementHandler,
                    "delegate");
            MappedStatement mappedStatement = (MappedStatement) ReflectHelper.getValueByFieldName(delegate,
                    "mappedStatement");
            /**
             * 第一层：通过过滤来区分是否需要分页 第二层：传入的参数是否是page对象或者map类型中含有page对象
             * 第三层：传入的参数是否有page参数/属性，如果有，则分页，
             */
            // 不需要分页的SQL
            if (this.contrainsException(mappedStatement.getId())) {
                return ivk.proceed();
            }
            BoundSql boundSql = delegate.getBoundSql();
            Object parameterObject = boundSql.getParameterObject();
            // 不存在传入值
            if (parameterObject == null) {
                return ivk.proceed();
            } else {
                Page page = null;
                if (parameterObject instanceof Page) { // 参数就是Page实体
                    page = (Page) parameterObject;
                } else if (parameterObject instanceof Map) {// 参数为包含page的Map对象
                    page = this.fetchPageFromMap((Map<Object, Object>) parameterObject);
                } else { // 参数为某个实体，该实体拥有Pages属性
                    page = ReflectHelper.getValueByFieldType(parameterObject, Page.class);
                }
                // 不存在分页参数
                if (page == null) {
                    return ivk.proceed();
                }
                // 处理sql
                String sql = boundSql.getSql();
                String pageSql = generatePagesSql(sql, page);
                ReflectHelper.setValueByFieldName(boundSql, "sql", pageSql);
                LOGGER.debug("execu sql:  " + pageSql);
            }
        }

        return ivk.proceed();
    }

    /**
     * Fetch Page From Map Object
     * 
     * @param paramMap
     * @return
     */

    @SuppressWarnings("rawtypes")
    private Page fetchPageFromMap(Map paramMap) {
        Page page = null;
        for (Entry entry : (Set<Entry>)paramMap.entrySet()) {
            if (entry.getValue() instanceof Page) {
                page = (Page) entry.getValue();
                break;
            }
        }
        return page;
    }

    /**
     * 根据数据库方言，生成特定的分页sql
     * 
     * @param sql
     * @param page
     * @return
     */
    private String generatePagesSql(String sql, Page page) {
        String resultSql = sql;
        if (page != null) {
            resultSql = this.dialect.buildPageSql(sql, page);
        }
        return resultSql;
    }

    /**
     * 判断是否包括不需要进行分页的sql 关键字
     * 
     * @param sqlId
     * @return
     */
    private boolean contrainsException(String sqlId) {
        if (StringHelper.isEmpty(sqlId)) {
            return false;
        }
        for (String keys : this.exceptKeywords) {
            if (sqlId.toLowerCase().indexOf(keys.toLowerCase().trim()) > -1) {
                return true;
            }
        }
        return false;
    }

    /**
     * 
     */
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    /**
     * 读取参数
     */
    @Override
    public void setProperties(Properties p) {
        String dbDialects = p.getProperty("dialect");
        String pageExceptKeywords = p.getProperty("exceptKeywords");
        // 判断数据库类型，设置方言
        if (StringHelper.isNotEmpty(dbDialects.trim())) {
            if ("mysql".equals(dbDialects)) {
                dialect = new MysqlDialect();
            } else if ("oracle".equals(dbDialects)) {
                dialect = new OracleDialect();
            }
            if (null == dialect) {
                dialect = new OracleDialect();
                LOGGER.info("No DB Dialect Detected, use Oracle as default");
            }
        }
        // 设置不需要进行分页的sql 关键字
        if (StringHelper.isNotEmpty(pageExceptKeywords)) {
            exceptKeywords = pageExceptKeywords.split(",");
        }
    }
}
